﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Configuration;
using System.Text;
using Navigant.Common;
using Navigant.Infrastructure.Cases;
using Navigant.Infrastructure.DomainBase;
using Navigant.Infrastructure.Helpers;
using Navigant.Infrastructure.UrlDictionary;
using Navigant.Model.Cases;
using Navigant.Model.Custodians;

namespace Navigant.Model.PreservationNotices
{
    public class PreservationNoticeTemplate : CaseChildItem, IAggregateRoot
    {
        #region Private Fields

        private string name;
        private string subject;
        private string body;
        private List<Attachment> attachments;
        private List<HtmlImageData> images;

        #endregion

        #region Constructors

        public PreservationNoticeTemplate(ICase parentCase, string name, string subject,
                  string body)
            : this(0, parentCase, name, subject, body)
        {
        }

        public PreservationNoticeTemplate(int id, ICase parentCase, string name,
            string subject, string body)
            : base(id, parentCase)
        {
            this.name = name;
            this.subject = subject;
            this.body = body;
            this.attachments = new List<Attachment>();
            this.images = new List<HtmlImageData>();
        }

        #endregion

        #region Properties

        public string Name
        {
            get { return this.name; }
        }

        public string Subject
        {
            get { return this.subject; }
            set { this.subject = value; }
        }

        public string Body
        {
            get { return this.body; }
            set { this.body = value; }
        }

        public ReadOnlyCollection<Attachment> Attachments
        {
            get { return this.attachments.AsReadOnly(); }
        }

        public ReadOnlyCollection<HtmlImageData> Images
        {
            get { return this.images.AsReadOnly(); }
        }

        #endregion

        #region Public Methods

        public void AddAttachment(Attachment attachment)
        {
            this.attachments.Add(attachment);
        }

        public void RemoveAttachment(Attachment attachment)
        {
            if (this.attachments.Contains(attachment))
            {
                this.attachments.Remove(attachment);
            }
        }

        public void AddImage(HtmlImageData image)
        {
            this.images.Add(image);
        }

        public void RemoveImage(HtmlImageData image)
        {
            if (this.images.Contains(image))
            {
                this.images.Remove(image);
            }
        }

        /// <summary>
        /// Merges a Custodian instance to form a new 
        /// PreservationNotice instance.
        /// </summary>
        /// <param name="custodian">The Custodian to merge.</param>
        /// <param name="reminder">True if the notice being created is for a reminder.</param>
        /// <param name="fromAddress">The email adress of the person sending the notice.</param>
        /// <returns>A new PreservationNotice instance.</returns>
        public PreservationNotice Merge(Custodian custodian, bool reminder,
            string fromAddress)
        {
            return this.Merge(custodian, reminder, fromAddress, false);
        }

        /// <summary>
        /// Merges a Custodian instance to form a new 
        /// PreservationNotice instance.
        /// </summary>
        /// <param name="custodian">The Custodian to merge.</param>
        /// <param name="reminder">True if the notice being created is for a reminder.</param>
        /// <param name="fromAddress">The email adress of the person sending the notice.</param>
        /// <param name="preview"> True is the notice being created is only for previewing purposes.</param>
        /// <returns>A new PreservationNotice instance.</returns>
        public PreservationNotice Merge(Custodian custodian, bool reminder,
            string fromAddress, bool preview)
        {
            if (custodian == null)
            {
                throw new ArgumentNullException("custodian",
                    "Must be a valid Custodian.");
            }

            if (string.IsNullOrEmpty(custodian.Email))
            {
                throw new ArgumentNullException("custodian",
                    "Custodian must have an email address.");
            }

            string messageIdentifier = Guid.NewGuid().ToString("N");
            string messageSubject = this.subject;
            if (reminder) messageSubject = string.Format("[Reminder] {0}", messageSubject);
            string messageBody = string.Format("<html><body>{0}</body></html>",
                this.AddLineBreaks(this.GetMessageBody(custodian,
                messageIdentifier, this.body, preview)));

            PreservationNotice notice = new PreservationNotice(this.ParentCase,
                custodian, fromAddress, messageSubject, messageBody,
                messageIdentifier, this, DateTime.Now);

            return notice;
        }

        public override string ToString()
        {
            return this.name;
        }

        #endregion

        #region Protected Methods

        protected override void Validate()
        {
        }

        protected override BrokenRuleMessages GetBrokenRuleMessages()
        {
            return null;
        }

        protected override void LoadExtendedProperties()
        {
        }

        #endregion

        #region Private Methods

        private string GetMessageBody(Custodian custodian, string messageIdentifier,
            string templateBody, bool preview)
        {
            StringBuilder stringBuilder = new StringBuilder(templateBody);

            // See if the acknowledgement should use the confirmation code
            bool acknowledgeWithCode = ConfigurationSettingsHelper.GetBoolean(custodian.ParentCase.ClientPortal,
                Constants.AcknowledgeWithCodeSetting);

            // The acknowledgement page is based on whether or not to use the confirmation code
            string acknowledgePageName = acknowledgeWithCode ? 
                Constants.AcknowledgeWithCodePageNameSetting : Constants.AcknowledgePageNameSetting;

            string acknowledgmentUrl = string.Empty;

            bool useSeparateAcknowledgmentSite = ConfigurationSettingsHelper.GetBoolean(custodian.ParentCase.ClientPortal,
                Constants.UseSeparateAcknowledgmentSiteSetting);

            if (useSeparateAcknowledgmentSite)
            {
                string sitePortNumber = ConfigurationManager.AppSettings[Constants.AcknowledgmentSitePortNumberSetting];
                if (string.IsNullOrEmpty(sitePortNumber))
                {
                    sitePortNumber = Constants.DefaultAcknowledgmentSitePortNumber;
                }
                string protocol = ConfigurationManager.AppSettings[Constants.ProtocolSetting];
                if (string.IsNullOrEmpty(protocol))
                {
                    protocol = Uri.UriSchemeHttps;
                }
                string host = ConfigurationManager.AppSettings[Constants.AcknowledgmentSiteHostNameSetting];
                if (string.IsNullOrEmpty(host))
                {
                    host = this.ParentCase.SiteAddress.Host;
                }
                acknowledgmentUrl = string.Format("{0}://{1}:{2}/{3}?case={4}&item={5}&siteCollection={6}",
                    protocol,
                    host,
                    sitePortNumber,
                    acknowledgePageName,
                    this.ParentCase.Id.ToString("N"),
                    messageIdentifier,
                    this.ParentCase.ClientPortalId.ToString("N"));
            }
            else
            {
                acknowledgmentUrl = string.Format("{0}://{1}/{2}/{3}?case={4}&item={5}&siteCollection={6}",
                    this.ParentCase.SiteAddress.Scheme,
                    this.ParentCase.SiteAddress.Host,
                    ConfigurationManager.AppSettings[Constants.AcknowledgmentVirtualDirectoryNameSetting],
                    acknowledgePageName,
                    this.ParentCase.Id.ToString("N"),
                    messageIdentifier,
                    this.ParentCase.ClientPortalId.ToString("N"));
            }

            // Get the shortened version of the URL
            acknowledgmentUrl = UrlDictionaryService.GetTinyUrl(acknowledgmentUrl, 
                custodian.ParentCase.ClientPortal);

            this.ProcessLinks(stringBuilder, acknowledgmentUrl);
            this.ProcessImages(stringBuilder, preview);

            // Replace all <div> tags with a <p> tag to fix the Outlook 2007 problem
            // where the page breaks made from the rich text editor do not 
            // render properly
            if (!preview && this.ParentCase.ClientPortal.FixLineBreaks)
            {
                stringBuilder.Replace("DIV>", "P>");
                stringBuilder.Replace("div>", "p>");
                stringBuilder.Replace("<P><FONT size=2></FONT>&nbsp;</P>", string.Empty);
            }

            string confirmationCode = messageIdentifier.Substring(0, Constants.ConfirmationCodeLength);
            string acknowledgementHtmlAnchor = string.Empty;
            if (acknowledgeWithCode)
            {
                acknowledgementHtmlAnchor = string.Format("Please click <a href=\"{0}\">here</a> to acknowledge this notice.  Your confirmation code is:  {1}",
                    acknowledgmentUrl, confirmationCode);
            }
            else
            {
                acknowledgementHtmlAnchor = string.Format("Please click <a href=\"{0}\">here</a> to acknowledge this notice.",
                    acknowledgmentUrl);
            }

            stringBuilder.Replace("[URL]", acknowledgementHtmlAnchor);
            stringBuilder.Replace("[Date]", DateTime.Now.ToLongDateString());
            stringBuilder.Replace("[Name]", custodian.Name.FullName);

            foreach (string property in custodian.ExtendedProperties.Keys)
            {
                stringBuilder.Replace("[" + property + "]", custodian.ExtendedProperties[property]);
            }

            return stringBuilder.ToString();
        }

        private string AddLineBreaks(string html)
        {
            StringBuilder stringBuilder = new StringBuilder();

            for (int index = 0; index < html.Length; index += 50)
            {
                int length = Math.Min(50, html.Length - index);

                string substring = html.Substring(index, length);

                int spaceIndex = substring.IndexOf(' ', substring.Length / 2);

                if (spaceIndex != -1 && substring.Substring(spaceIndex - 2, 2) == "<a")
                {
                    int nextSpaceIndex = substring.IndexOf(' ', spaceIndex + 1);
                    if (nextSpaceIndex == -1)
                    {
                        spaceIndex = -1;
                    }
                    else
                    {
                        spaceIndex = spaceIndex + nextSpaceIndex + 1;
                    }
                }                
                if (spaceIndex != -1)
                {
                    try
                    {
                        stringBuilder.Append(substring.Substring(0, spaceIndex));
                        stringBuilder.Append("\r\n");
                        stringBuilder.Append(substring.Substring(spaceIndex + 1));
                    }
                    catch (Exception e)
                    {
                        Logger.LogError(e, this.ParentCase);
                    }
                }
                else
                {
                    stringBuilder.Append(substring);
                }                
            }

            return stringBuilder.ToString();
        }

        private void ProcessImages(StringBuilder builder, bool preview)
        {
            string remainingBody = this.body;
            int index = 0;
            string token = string.Empty;
            string tokenImageName = string.Empty;
            string imageSource = string.Empty;
            string imageHtmlTag = string.Empty;
            
            while (true)
            {
                index = remainingBody.IndexOf("[Image|");
                if (index == -1) break;

                remainingBody = remainingBody.Substring(index);
                index = remainingBody.IndexOf("]");

                if (index == -1) break;

                token = remainingBody.Substring(0, index + 1);
                tokenImageName = remainingBody.Substring(7, index - 7);
                

                foreach (HtmlImageData image in this.ParentCase.Images)
                {
                    if (image.Name.Trim().ToLower() == tokenImageName.Trim().ToLower())
                    {
                        if (preview)
                        {
                            imageSource = this.ParentCase.SiteAddress.AbsoluteUri + "/" + image.RelativeUrl;
                            imageHtmlTag = string.Format("<img src=\"{0}\" width=\"{1}\" height=\"{2}\">",
                                imageSource, image.Width, image.Height);
                        }
                        else
                        {
                            imageSource = string.Format("cid:{0}", image.Name);
                            imageHtmlTag = string.Format("<img src=\"{0}\">", imageSource);
                        }

                        builder.Replace(token, imageHtmlTag);
                        this.images.Add(image);
                    }
                }
                remainingBody = remainingBody.Substring(index + 1);
            }
        }

        private void ProcessLinks(StringBuilder stringBuilder, string acknowledgeUrl)
        {
            string remainingBody = stringBuilder.ToString();
            int index = 0;
            string urlTokenValuesString = string.Empty;
            string urlToken = string.Empty;
            string[] urlTokenValues = null;
            string label = string.Empty;
            string url = string.Empty;
            string anchor = string.Empty;

            while (true)
            {
                index = remainingBody.IndexOf("[URL|", StringComparison.CurrentCultureIgnoreCase);
                if (index == -1) break;

                remainingBody = remainingBody.Substring(index);

                index = remainingBody.IndexOf("]");
                if (index == -1) break;

                urlTokenValuesString = remainingBody.Substring(5, index - 5);
                urlToken = remainingBody.Substring(0, index + 1);

                urlTokenValues = urlTokenValuesString.Split('|');

                if (urlTokenValues.Length > 0)
                {
                    label = urlTokenValues[0];
                    url = urlTokenValues.Length > 1 ? urlTokenValues[1] : acknowledgeUrl;
                    anchor = this.BuildAnchor(url, label);
                    stringBuilder.Replace(urlToken, anchor);
                }
                remainingBody = remainingBody.Substring(index + 1);
            }
        }

        private string BuildAnchor(string url, string label)
        {
            bool isBold = url.IndexOf("<strong>") > -1;
            bool isUnderline = url.IndexOf("<u>") > -1;
            bool isItalic = url.IndexOf("<em>") > -1;
            int fontStart = url.IndexOf("<font");
            string fontTag = string.Empty;

            if (fontStart > -1)
            {
                int fontEnd = url.IndexOf(">", fontStart);
                if (fontEnd > -1)
                {
                    fontTag = url.Substring(fontStart, fontEnd - fontStart + 1);
                }
            }

            url = url.Replace("<strong>", string.Empty);
            url = url.Replace("</strong>", string.Empty);
            url = url.Replace("<u>", string.Empty);
            url = url.Replace("</u>", string.Empty);
            url = url.Replace("<em>", string.Empty);
            url = url.Replace("</em>", string.Empty);
            url = url.Replace("</font>", string.Empty);

            if (fontTag.Length > 0)
            {
                url = url.Replace(fontTag, string.Empty);
            }

            StringBuilder anchor = new StringBuilder(@"<a href=""");
            anchor.Append(url);
            anchor.Append(@""">");
            if (isBold)
            {
                anchor.Append("<strong>");
            }
            if (isUnderline)
            {
                anchor.Append("<u>");
            }
            if (isItalic)
            {
                anchor.Append("<em>");
            }
            if (fontTag.Length > 0)
            {
                anchor.Append(fontTag);
            }
            anchor.Append(label);
            if (fontTag.Length > 0)
            {
                anchor.Append("</font>");
            }
            if (isItalic)
            {
                anchor.Append("</em>");
            }
            if (isUnderline)
            {
                anchor.Append("</u>");
            }
            if (isBold)
            {
                anchor.Append("</strong>");
            }
            anchor.Append("</a>");
            return anchor.ToString();
        }

        #endregion
    }
}
